﻿using System;
using System.Diagnostics.Contracts;
using Isuka.Iteration.Auxiliaries;

namespace Isuka.Iteration
{
    public static class LinqToIterable
    {
        public static bool Any(this IIterable<IIterator> source)
        {
            Contract.Requires<ArgumentNullException>(source != null, "source");

            var first = source.Begin();
            return (first.InRange && !first.Equals(source.End()));
        }

        public static IIterable<IIterator<T>> ForEach<T, TResult>(this IIterable<IIterator<T>> source, Action<IIterator<T>, IIterator<T>> action)
        {
            Contract.Requires<ArgumentNullException>(source != null, "source");
            Contract.Requires<ArgumentNullException>(action != null, "action");
            Contract.Ensures(Contract.Result<IIterable<IIterator<T>>>() != null);

            var first = source.Begin();
            var last = source.End();
            for (var i = first.Copy(); i.InRange && !i.Equals(last); i.MoveNext())
            {
                action(i, last.Copy());
            }
            return source;
        }

        public static IIterable<IIterator<T>> ForEach<T, TResult>(this IIterable<IIterator<T>> source, Action<T> action)
        {
            Contract.Requires<ArgumentNullException>(source != null, "source");
            Contract.Requires<ArgumentNullException>(action != null, "action");
            Contract.Ensures(Contract.Result<IIterable<IIterator<T>>>() != null);

            var first = source.Begin();
            var last = source.End();
            for (var i = first.Copy(); i.InRange && !i.Equals(last); i.MoveNext())
            {
                action(i.Current);
            }
            return source;
        }

        public static IIterable<WrappedIterator<TSrc, TDst>>
            Select<TSrc, TDst>(IIterable<IIterator<TSrc>> source, Func<TSrc, TDst> selector)
        {
            Contract.Requires<ArgumentNullException>(source != null, "source");
            Contract.Requires<ArgumentNullException>(selector != null, "selector");
            Contract.Ensures(Contract.Result<IIterable<WrappedIterator<TSrc, TDst>>>() != null);

            var traits = new MapTraits<TSrc, TDst>(selector);
            return new AnonymousIterable<WrappedIterator<TSrc, TDst>>(
                () => new WrappedIterator<TSrc, TDst>(source, source.Begin(), traits)
                , () => new WrappedIterator<TSrc, TDst>(source, source.End(), traits));
        }

        public static void WhenEmpty(IIterable<IIterator> source, Action action)
        {
            Contract.Requires(source != null);
            Contract.Requires<ArgumentNullException>(action != null, "action");

            if (Any(source))
            {
                action();
            }
        }

        public static IIterable<WrappedIterator<T, T>>
            Where<T>(IIterable<IIterator<T>> source, Func<T, bool> predicate)
        {
            Contract.Requires<ArgumentNullException>(source != null, "source");
            Contract.Requires<ArgumentNullException>(predicate != null, "predicate");
            Contract.Ensures(Contract.Result<IIterable<WrappedIterator<T, T>>>() != null);

            var traits = new FilterTraits<T>(predicate);
            return new AnonymousIterable<WrappedIterator<T, T>>(
                () => new WrappedIterator<T, T>(source, source.Begin(), traits)
                , () => new WrappedIterator<T, T>(source, source.End(), traits));
        }
    }
}
